package com.zhiche.lisa.bms.service.utils.common;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.annotations.TableField;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @Author: liuanshun
 * @Description: 提供实体和属性字段列转换的工具类
 * @Date: Create in 11:46 2018/9/11
 */
public class BeanColumnUtil<T1 extends Object, T2 extends Object> {
	/**
	 * 检查列
	 *
	 * @param entity1
	 * @param entity2
	 * @return 不相同的列集合
	 * @throws IllegalAccessException   如果{@code Field}对象强制执行Java语言访问控制，则基础字段不可访问。
	 * @throws IllegalArgumentException 如果指定的对象不是声明底层字段的类或接口的实例（或其子类或实现者）。
	 */
	public List<String> checkColumn(T1 entity1, T2 entity2)
			throws IllegalArgumentException, IllegalAccessException {
		List<String> result = Lists.newArrayList();
		Field[] fs = entity1.getClass().getDeclaredFields();
		//获取所有属性
		for (Field f : fs) {
			if (!this.checkEntityByField(entity1, entity2, f)) {
				result.add(f.getName());
			}
		}
		return result;
	}

	/**
	 * 检查列
	 *
	 * @param entity1
	 * @param entity2
	 * @return 不相同的列集合
	 * @throws IllegalAccessException   如果{@code Field}对象强制执行Java语言访问控制，则基础字段不可访问。
	 * @throws IllegalArgumentException 如果指定的对象不是声明底层字段的类或接口的实例（或其子类或实现者）。
	 */
	public List<String> checkColumnInAppoint(T1 entity1, T2 entity2, List<String> columnName)
			throws IllegalArgumentException, IllegalAccessException {
		List<String> result = Lists.newArrayList();
		Field[] fs = entity1.getClass().getDeclaredFields();
		//获取所有属性
		for (Field f : fs) {
			if (columnName.contains(f.getName())) {
				if (!this.checkEntityByField(entity1, entity2, f)) {
					result.add(f.getName());
				}
			}
		}
		return result;
	}

	/**
	 * 根据Field寻找匹配实体的结果是否相等
	 *
	 * @param entity1
	 * @param entity2
	 * @param f1
	 * @return true相同，false不相同
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	private boolean checkEntityByField(T1 entity1, T2 entity2, Field f1)
			throws IllegalArgumentException, IllegalAccessException {
		f1.setAccessible(true);//设置访问性，反射类的方法，设置为true就可以访问private修饰的东西，否则无法访问
		Field f2 = null;
		try {
			f2 = entity2.getClass().getDeclaredField(f1.getName());
			f2.setAccessible(true);
		} catch (NoSuchFieldException e) {
			//Different columns
		}
		if (Objects.nonNull(f2)) {
			Object v1 = f1.get(entity1);
			Object v2 = f2.get(entity2);
			if (!Objects.deepEquals(v1, v2)) {
				if (v1 != null && v2 != null) {
					String typeName1 = f1.getType().getTypeName();
					String typeName2 = f2.getType().getTypeName();
					if ("java.math.BigDecimal".equals(typeName1) || "java.math.BigDecimal".equals(typeName2)) {
						int comVlu = new BigDecimal(v1.toString()).compareTo(new BigDecimal(v2.toString()));
						if (comVlu == 0) {
							return true;
						}
					}
				}
				return false;
			}
		}
		return true;
	}

	/**
	 * 将对象转换成Map
	 *
	 * @param obj 需要转化的对象
	 * @return 转换后的HashMap（结果移除NullValue的列）
	 */
	public static Map<String, Object> objectToMap(Object obj) {
		return objectToMap(obj, false);
	}

	/**
	 * 将对象转换成Map
	 *
	 * @param obj           需要转化的对象
	 * @param allowableNull 是否允许空列:true,允许返回值map将拥有NullValue;false,不允许返回值将移除NullValue
	 * @return 转换后的HashMap（结果移除NullValue的列）
	 */
	public static Map<String, Object> objectToMap(Object obj, boolean allowableNull) {
		Map<String, Object> map = new HashMap<String, Object>();
		Map<String, String> tableFieMap = new HashMap<>();
		// 获取f对象对应类中的所有属性域
		Field[] fields = obj.getClass().getDeclaredFields();
		for (Field field :
				fields) {
			String varName = field.getName();
			try {
				// 获取原来的访问控制权限
				boolean accessFlag = field.isAccessible();
				// 修改访问控制权限
				field.setAccessible(true);
				// 获取在对象f中属性fields[i]对应的对象中的变量
				Object o = field.get(obj);
				if (!allowableNull) {
					if (o == null) continue;
				}
				map.put(varName, o);
				if (field.isAnnotationPresent(TableField.class)) {
					tableFieMap.put(varName, field.getAnnotation(TableField.class).value());
				}
				// 恢复访问控制权限
				field.setAccessible(accessFlag);
			} catch (IllegalArgumentException ex) {
				ex.printStackTrace();
			} catch (IllegalAccessException ex) {
				ex.printStackTrace();
			}
		}
		if (!CollectionUtils.isEmpty(tableFieMap)) {
			map.put("$tableFieMap", tableFieMap);
		}
		return map;
	}


	/**
	 * 修改对象指定字段
	 *
	 * @param obj    指定对象
	 * @param cloumn 字段名
	 * @param value  值
	 * @return
	 * @throws NoSuchFieldException
	 */
	public BeanColumnUtil setColumn(Object obj, String cloumn, Object value) {
		if (Strings.isNullOrEmpty(cloumn)) throw new NullPointerException();
		// 获取f对象对应类中的所有属性域
		try {
			Field field = obj.getClass().getDeclaredField(cloumn);
			// 获取原来的访问控制权限
			boolean accessFlag = field.isAccessible();
			// 修改访问控制权限
			field.setAccessible(true);
			//修改指定结果
			Class<?> toTypeVal = field.getType();
			if (value == null) {
				field.set(obj, new Object());
			} else {
				field.set(obj, JSONObject.parseObject(value.toString(), toTypeVal));
			}
			// 恢复访问控制权限
			field.setAccessible(accessFlag);
		} catch (NoSuchFieldException ex) {
			ex.printStackTrace();
		} catch (IllegalAccessException ex) {
			ex.printStackTrace();
		}
		return this;
	}

	/**
	 * 获取对象指定字段
	 *
	 * @param obj    指定对象
	 * @param cloumn 字段名
	 * @return 结果
	 * @throws NoSuchFieldException
	 */
	public Object getColumn(Object obj, String cloumn) throws NoSuchFieldException {
		Object o = null;
		if (Strings.isNullOrEmpty(cloumn)) throw new NullPointerException();
		// 获取f对象对应类中的所有属性域
		try {
			Field field = obj.getClass().getDeclaredField(cloumn);
			// 获取原来的访问控制权限
			boolean accessFlag = field.isAccessible();
			// 修改访问控制权限
			field.setAccessible(true);
			//修改指定结果
			o = field.get(obj);
			// 恢复访问控制权限
			field.setAccessible(accessFlag);
		} catch (IllegalArgumentException ex) {
			ex.printStackTrace();
		} catch (IllegalAccessException ex) {
			ex.printStackTrace();
		}
		return o;
	}
}
